import sys ,types, string, pprint, exceptions
from signal import *
from ctypes import *
from enum import Enum

#(*pa_context_subscribe_cb_t)(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata);
#(*pa_context_success_cb_t) (pa_context *c, int success, void *userdata);

state_cb_func = CFUNCTYPE(c_void_p, c_void_p)
subscribe_cb_func = CFUNCTYPE(c_void_p, c_int, c_int, c_void_p)
subscribe_success_cb_func = CFUNCTYPE(c_void_p, c_int, c_void_p)

class Pulse:
    enum_context_state= Enum("PA_CONTEXT_UNCONNECTED",    #/**< The context hasn't been connected yet */
    "PA_CONTEXT_CONNECTING",     #/**< A connection is being established */
    "PA_CONTEXT_AUTHORIZING",    #/**< The client is authorizing itself to the daemon */
    "PA_CONTEXT_SETTING_NAME",   #/**< The client is passing its application name to the daemon */
    "PA_CONTEXT_READY",          #/**< The connection is established, the context is ready to execute operations */
    "PA_CONTEXT_FAILED",         #/**< The connection failed or was disconnected */
    "PA_CONTEXT_TERMINATED")      #/**< The connection was terminated cleanly */
    
    enum_error = Enum("PA_OK",       #/**< No error */
    "PA_ERR_ACCESS",                 #/**< Access failure */
    "PA_ERR_COMMAND",                #/**< Unknown command */
    "PA_ERR_INVALID",                #/**< Invalid argument */
    "PA_ERR_EXIST",                  #/**< Entity exists */
    "PA_ERR_NOENTITY",               #/**< No such entity */
    "PA_ERR_CONNECTIONREFUSED",      #/**< Connection refused */
    "PA_ERR_PROTOCOL",               #/**< Protocol error */
    "PA_ERR_TIMEOUT",                #/**< Timeout */
    "PA_ERR_AUTHKEY",                #/**< No authorization key */
    "PA_ERR_INTERNAL",               #/**< Internal error */
    "PA_ERR_CONNECTIONTERMINATED",   #/**< Connection terminated */
    "PA_ERR_KILLED",                 #/**< Entity killed */
    "PA_ERR_INVALIDSERVER",          #/**< Invalid server */
    "PA_ERR_MODINITFAILED",          #/**< Module initialization failed */
    "PA_ERR_BADSTATE",               #/**< Bad state */
    "PA_ERR_NODATA",                 #/**< No data */
    "PA_ERR_VERSION",                #/**< Incompatible protocol version \since 0.8 */
    "PA_ERR_TOOLARGE",               #/**< Data too large \since 0.8.1 */
    "PA_ERR_NOTSUPPORTED",           #/**< Operation not supported \since 0.9.5 */
    "PA_ERR_MAX")                    #/**< Not really an error but the first invalid error code */
    
    enum_subs_mask = Enum("PA_SUBSCRIPTION_MASK_NULL",               #/**< No events */
    "PA_SUBSCRIPTION_MASK_SINK", #= 1,               #/**< Sink events */
    "PA_SUBSCRIPTION_MASK_SOURCE", #= 2,             #/**< Source events */
    "PA_SUBSCRIPTION_MASK_SINK_INPUT", #= 4,         #/**< Sink input events */
    "PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT", #= 8,      #/**< Source output events */
    "PA_SUBSCRIPTION_MASK_MODULE", #= 16,            #/**< Module events */
    "PA_SUBSCRIPTION_MASK_CLIENT", #= 32,            #/**< Client events */
    "PA_SUBSCRIPTION_MASK_SAMPLE_CACHE", #= 64,      #/**< Sample cache events */
    "PA_SUBSCRIPTION_MASK_SERVER", #= 128,           #/**< Other global server changes. \since 0.4 */
    "PA_SUBSCRIPTION_MASK_AUTOLOAD") #= 256,         #/**< Autoload table events. \since 0.5 */
    #PA_SUBSCRIPTION_MASK_ALL = 511               #/**< Catch all events \since 0.8 */
    
    def __init__(self,name):
        self.__name__ = name
        
        #cdll.LoadLibrary("libpulse.so.0")
        self.libpulse = CDLL("libpulse.so.0")
        self.libglib = CDLL("libglib-2.0.so.0")
        self.libgmloop = CDLL("libpulse-mainloop-glib.so.0")
        
        self.mainloop = None
        self.api = None
        self.context = None
        #Using c_void_p for now or maybe there is no need for else
        self.libglib.g_main_context_default.restype = c_void_p
        
        self.libgmloop.pa_glib_mainloop_get_api.restype = c_void_p
        self.libgmloop.pa_glib_mainloop_get_api.argtypes = [c_void_p]
        self.libgmloop.pa_glib_mainloop_new.argtypes = [c_void_p]
        self.libgmloop.pa_glib_mainloop_new.restype = c_void_p
        self.libgmloop.pa_glib_mainloop_free.argtypes = [c_void_p]
        self.libgmloop.pa_threaded_mainloop_new.restype = c_void_p
        
        self.libgmloop.pa_context_unref.argtypes = [c_void_p]
        self.libgmloop.pa_context_new.argtypes = [c_void_p, c_char_p]
        self.libgmloop.pa_context_get_state.argtypes = [c_void_p]
        self.libgmloop.pa_context_get_state.restype = c_int
        self.libgmloop.pa_context_set_state_callback.argtypes = [c_void_p, state_cb_func, c_void_p]
        self.libgmloop.pa_context_set_state_callback.restype = c_void_p
        self.libgmloop.pa_context_set_subscribe_callback.argtypes = [c_void_p, subscribe_cb_func, c_void_p]
        self.libgmloop.pa_context_set_subscribe_callback.restype = c_void_p
        self.libgmloop.pa_context_connect.argtypes = [c_void_p,c_void_p,c_int,c_void_p]
        self.libgmloop.pa_context_connect.restype = c_int
        self.libgmloop.pa_context_subscribe.argtypes = [c_void_p, c_int, subscribe_success_cb_func, c_void_p]
        self.libgmloop.pa_context_subscribe.restype = c_void_p
        self.libgmloop.pa_strerror.restype = c_char_p
        self.libgmloop.pa_context_new.restype = c_void_p
        self.libgmloop.pa_context_new.argtypes = [c_void_p, c_char_p]
        self.libgmloop.pa_threaded_mainloop_get_api.argtypes = [c_void_p]
        self.libgmloop.pa_threaded_mainloop_get_api.restype = c_void_p
        self.libgmloop.pa_threaded_mainloop_start.argtypes = [c_void_p]
        self.libgmloop.pa_threaded_mainloop_lock.argtypes = [c_void_p]
        self.libgmloop.pa_threaded_mainloop_unlock.argtypes = [c_void_p]
        self.libgmloop.pa_threaded_mainloop_wait.argtypes = [c_void_p]
        self.libgmloop.pa_threaded_mainloop_signal.argtypes = [c_void_p, c_int]
        
        
        self.libgmloop.pa_operation_unref.argtypes = [c_void_p]
        self.libgmloop.pa_operation_unref.restype = c_void_p
        
        self.libpulse.pa_get_library_version.restype = c_char_p
        self.libpulse.pa_bytes_snprint.restype = c_char_p
        
        #Set up PulseAudio
        self.setup_thread("PulseVol")
        self.set_state_callback(self.py_state_cb, None)
        res = self.connect_context()
        

    
    def __del__(self):
        self.free()

    def formatbytes(self,size):
        return self.pulselibpulse.pa_bytes_snprint(create_string_buffer(256),256,size)

    def py_subscribe_cb(pulse,context,eventtype,idx,userdata = None):
        print "py_subscribe_cb", eventtype
        return 0
    
    def py_success_cb(pulse,context,user):
        pulse.libgmloop.pa_threaded_mainloop_signal(pulse.mainloop, 0);
    
    def py_state_cb(pulse,context,userdata = None):
        state = pulse.get_context_state()
        print "py_state_cb", state
        if state == pulse.enum_context_state.PA_CONTEXT_READY or state == pulse.enum_context_state.PA_CONTEXT_TERMINATED or state == pulse.enum_context_state.PA_CONTEXT_FAILED: 
            pulse.libgmloop.pa_threaded_mainloop_signal(pulse.mainloop, 0);

        return
        
    def get_context_state(self):
        return self.enum_context_state[ self.libgmloop.pa_context_get_state(self.context) ]

    def version(self):
        return self.libpulse.pa_get_library_version()
        
    def get_context(self):
        return self.libglib.g_main_context_default()
        
    def setup_thread(self,name):
        
        self.mainloop = self.libgmloop.pa_threaded_mainloop_new()
        if not self.mainloop:
            print "Loop:",self.get_error()
            return self.unlock_and_fail()

        self.libgmloop.pa_threaded_mainloop_lock(self.mainloop)
        
        self.api = self.libgmloop.pa_threaded_mainloop_get_api(self.mainloop)
        if not self.api:
            print "Api:",self.get_error()
            return self.unlock_and_fail()
            
        self.context = self.libgmloop.pa_context_new(self.api,name)
        if not self.context:
            print "Context:",self.get_error()
            return self.unlock_and_fail()
            
        self.set_state_callback(self.py_state_cb,None)
        self.set_subscribe_callback(self.py_subscribe_cb,None)
        
        if self.connect_context() < 0:
            print "Connect:",self.get_error()
            return self.unlock_and_fail()
        
        if self.libgmloop.pa_threaded_mainloop_start(self.mainloop) <0:
            print "Loop start:",self.get_error()
            return self.unlock_and_fail()
            #self.libgmloop.pa_threaded_mainloop_wait(self.mainloop)
        print "State:",self.get_context_state()
        
        if self.get_context_state() == self.enum_context_state.PA_CONTEXT_CONNECTING:
            #print "subs cb:",pulse.set_subscribe_callback(context,pulse.py_subscribe_cb,None)
            mask = (2** (self.enum_subs_mask.PA_SUBSCRIPTION_MASK_SINK.index -1) |
                    2** (self.enum_subs_mask.PA_SUBSCRIPTION_MASK_SOURCE.index -1) |
                    2** (self.enum_subs_mask.PA_SUBSCRIPTION_MASK_SINK_INPUT.index -1) |
                    2** (self.enum_subs_mask.PA_SUBSCRIPTION_MASK_CLIENT.index -1)|
                    2** (self.enum_subs_mask.PA_SUBSCRIPTION_MASK_SERVER.index -1))
            print mask
            op = self.subscribe(self.context,mask,self.py_success_cb,None)
            print "op:", op, self.get_error()
        
        print "Exiting setup"
        return True
        
    def unlock_and_fail(self):
        self.libgmloop.pa_threaded_mainloop_unlock(self.mainloop)
        return False
        
    def free(self):
        self.libgmloop.pa_context_unref(self.context)
        self.libgmloop.pa_glib_mainloop_free(self.mainloop)
        
    def set_state_callback(self,cb,userdata):
        self.libgmloop.pa_context_set_state_callback(self.context,state_cb_func(cb),None)
    
    def set_subscribe_callback(self,cb,userdata=None):
        return self.libgmloop.pa_context_set_subscribe_callback(self.context,subscribe_cb_func(cb),None)
        
    def connect_context(self):
        return self.libgmloop.pa_context_connect(self.context,None,0,None)
    
    def subscribe(self,cxt,mask,cb = None,user = None):
        return self.libgmloop.pa_context_subscribe(cxt,mask,subscribe_success_cb_func(cb),user)
    
    def operation_unref(self,op):
        self.libgmloop.pa_operation_unref(op)
        
    def get_error(self):
        return self.libgmloop.pa_strerror(self.libgmloop.pa_context_errno(self.context))
    
signal(SIGPIPE,SIG_IGN)
p = Pulse("PulseVol")
print "PA version:",p.version()
print "addr:",p.context, p.mainloop, p.api
